/*
 * LocalMultiply.java
 *
 * Created on January 10, 2005, 12:48 AM
 */

package edu.temple.GUS.MMA.Functions;

import java.util.ArrayList;
import edu.temple.GUS.MMA.DataTypes.*;
import edu.temple.GUS.MMA.Neighborhoods.*;
import edu.temple.GUS.MMA.util.StatisticalOperator;
import edu.temple.GUS.MMA.Exceptions.MMAFunctionInitException;

/**
 *
 * @author  Jeff Leong
 */
public class LocalMultiply {
    
    /** Creates a new instance of LocalMultiply */
    public LocalMultiply() {}
    
        
    public MMATimeSeries execute(MMATimeSeries tSeries, float value) throws MMAFunctionInitException
    {
        if (tSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");
        
        // Initialze the t dimension of the timeseries and the noData value
        int t = tSeries.getT();
        float noData = tSeries.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        
        for ( int i=0; i <t; i++)
        {
            if (tSeries.getElement(i) == noData)
                resultData[i] = noData;
            else
                resultData[i] = tSeries.getElement(i) * value;
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute(MMATimeSeries tSeries1, MMATimeSeries tSeries2) throws MMAFunctionInitException
    {
        if ((tSeries1 == null) || (tSeries2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series are not initialized.");

        // Initialze the t dimension of timeseries 1 and the noData value
        int t = tSeries1.getT();
        float noData = tSeries1.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
                
        // Ensure the time series have the identical dimensions
        if (t != tSeries2.getT())
            throw new MMAFunctionInitException(this.getClass().toString() + " Boundaries mismatch.");
            
        for ( int i=0; i <t; i++)
        {
            if ((tSeries1.getElement(i) == noData) || (tSeries2.getElement(i) == noData))
                resultData[i] = noData;
            else
                resultData[i] = tSeries1.getElement(i) * tSeries2.getElement(i);
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute( MMATimeSeriesList timeSeriesList ) throws MMAFunctionInitException
    {
        MMATimeSeries baseTSeries;
        MMATimeSeries tempTSeries;

        if (timeSeriesList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is not initialized.");

        // Make sure the list is not empty
        if (timeSeriesList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is empty");
        
        // Return the fist element if there is only one time series in the list
        if (timeSeriesList.size() == 1)
            return (MMATimeSeries)timeSeriesList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseTSeries = (MMATimeSeries)timeSeriesList.get(0);
        
        // Initialze the dimensions of time series 1 and the noData value
        int t = baseTSeries.getT();
        float noData = baseTSeries.getNoData();
        int index;

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        float temp;
        
        // Ensure the time series have the identical dimensions
        for ( index=1; index < timeSeriesList.size(); index++)
        {
            tempTSeries = (MMATimeSeries)timeSeriesList.get(index);
            if (t != tempTSeries.getT())
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int i=0; i <t; i++)
        {
            // Check for noData value
            if (baseTSeries.getElement(i) == noData)
                resultData[i] = noData;
            else
            {
                // Initialize temp with value of the i-th element of the base time series
                temp = baseTSeries.getElement(i);

                for ( index=1; index < timeSeriesList.size(); index++)
                {
                    tempTSeries = (MMATimeSeries)timeSeriesList.get(index);

                    // Check for noData value for each time series element
                    if (tempTSeries.getElement(i) == noData)
                    {
                        resultData[i] = noData;
                        break;
                    }
                    else
                        // Add value of an element of the current time series to temp
                        temp *= tempTSeries.getElement(i);
                }

                if (resultData[i] != noData)
                    resultData[i] = temp;
            }
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute(MMATimeSeries tSeries1, MMATimeSeries tSeries2, TLag lag) throws MMAFunctionInitException
    {
            if ((tSeries1 == null) || (tSeries2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series are not initialized.");

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Initialze the t dimension of timeseries 1 and the noData value
        int t = tSeries1.getT();
        int tOffset;
        float noData = tSeries1.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        
        tOffset = lag.getT();

        // Ensure the time series have the identical dimensions
        if (t != tSeries2.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int i=0; i <t; i++)
        {
            // Check if the index is within the boundary
            if (((tOffset + i) < 0 ) || ((tOffset + i) >= t ))
                resultData[i] = noData;
            else
                if ((tSeries1.getElement(i) == noData) || (tSeries2.getElement(tOffset + i) == noData))
                    resultData[i] = noData;
                else
                    resultData[i] = tSeries1.getElement(i) * tSeries2.getElement(tOffset + i);
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute( MMATimeSeriesList timeSeriesList, TLag lag) throws MMAFunctionInitException
    {
        MMATimeSeries baseTSeries;
        MMATimeSeries tempTSeries;

        if (timeSeriesList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is not initialized.");

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Make sure the list is not empty
        if (timeSeriesList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is empty");
        
        // Return the fist element if there is only one time series in the list
        if (timeSeriesList.size() == 1)
            return (MMATimeSeries)timeSeriesList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseTSeries = (MMATimeSeries)timeSeriesList.get(0);
        
        // Initialze the dimensions of time series 1 and the noData value
        int t = baseTSeries.getT();
        int tOffset;
        float noData = baseTSeries.getNoData();
        int index;

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        float temp;
        
        // Ensure the time series have the identical dimensions
        for ( index=1; index < timeSeriesList.size(); index++)
        {
            tempTSeries = (MMATimeSeries)timeSeriesList.get(index);
            if (t != tempTSeries.getT())
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        tOffset = lag.getT();

        for ( int i=0; i <t; i++)
        {
            // Check if the index is within the boundary
            if (((tOffset + i) < 0 ) || ((tOffset + i) >= t ))
                resultData[i] = noData;
            else
                // Check for noData value
                if (baseTSeries.getElement(i) == noData)
                    resultData[i] = noData;
                else
                {
                    // Initialize temp with value of the i-th element of the base time series
                    temp = baseTSeries.getElement(i);

                    for ( index=1; index < timeSeriesList.size(); index++)
                    {
                        tempTSeries = (MMATimeSeries)timeSeriesList.get(index);

                        // Check for noData value for each time series element
                        if (tempTSeries.getElement(tOffset + i) == noData)
                        {
                            resultData[i] = noData;
                            break;
                        }
                        else
                            // Multiply value of an element of the current time series with temp
                            temp *= tempTSeries.getElement(tOffset + i);
                    }

                    if (resultData[i] != noData)
                        resultData[i] = temp;
                }
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;

    }

    public MMATimeSeries execute( MMATimeSeriesList timeSeriesList, TLagList lagList) throws MMAFunctionInitException
    {
        MMATimeSeries baseTSeries;
        MMATimeSeries tempTSeries;
        TLag tempLag;
        
        if (timeSeriesList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is not initialized.");

        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is not initialized.");
        
        // Make sure the list is not empty
        if (timeSeriesList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list is empty");
        
        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is empty");

        // Make sure the lag list has the same number of element as the time series list
        if (timeSeriesList.size() != (lagList.size()))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series list and lag list don't have the same number of elemtents");

        // Return the fist element if there is only one time series in the list
        if (timeSeriesList.size() == 1)
            return (MMATimeSeries)timeSeriesList.get(0);

        // Make sure the lag list has the same number of element as the time series list
        if (timeSeriesList.size() != (lagList.size()))
            return null;

        // Retrieve the first element from the list to be the base time series
        baseTSeries = (MMATimeSeries)timeSeriesList.get(0);
        
        // Initialze the dimensions of the first time series and the noData value
        int t = baseTSeries.getT();
        int tOffset;
        int baseTOffset;
        float noData = baseTSeries.getNoData();
        int index;

        // initialize the baseTOffset
        tempLag = (TLag)lagList.get(0);
        baseTOffset = tempLag.getT();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        float temp;
        
        // Ensure the time series have the identical dimensions
        for ( index=1; index < timeSeriesList.size(); index++)
        {
            // Let tempTSeries to store the index-th element of time series list
            tempTSeries = (MMATimeSeries)timeSeriesList.get(index);
            if (t != tempTSeries.getT())
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int i=0; i <t; i++)
        {
            // Check for noData value
            // Check if the index for the first timeseries is within the boundary
            if (((i+baseTOffset) < 0 ) || ((i+baseTOffset) >= t ))
                resultData[i] = noData;
            else
                {
                if (baseTSeries.getElement(i+baseTOffset) == noData)
                    resultData[i] = noData;
                else
                {
                    // Initialize temp with value of the i-th element of the base time series
                    temp = baseTSeries.getElement(i+baseTOffset);

                    for ( index=1; index < timeSeriesList.size(); index++)
                    {
                        tempTSeries = (MMATimeSeries)timeSeriesList.get(index);
                        tempLag = (TLag)lagList.get(index);
                        tOffset = tempLag.getT();

                        // Check if the index is within the boundary
                        if (((tOffset + i) < 0 ) || ((tOffset + i) >= t ))
                            resultData[i] = noData;
                        else
                            // Check for noData value for each time series element
                            if (tempTSeries.getElement(tOffset + i) == noData)
                            {
                                resultData[i] = noData;
                                break;
                            }
                            else
                                // Multiply value of an element of the current time series with temp
                                temp *= tempTSeries.getElement(tOffset + i);
                    }

                    if (resultData[i] != noData)
                        // Assign final value
                        resultData[i] = temp;
                }
            }
        }
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
       
    }
    
    /* MMA Grid Functions */
    public MMAGrid execute(MMAGrid grid, float value) throws MMAFunctionInitException
    {       
        if ( grid == null )
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");
            
        // Initialize x, y, data, noData
        int x = grid.getX();
        int y = grid.getY();
        float noData = grid.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
                // Check for no data value
                if ((grid.getElement(i, j) == noData) || (value == noData))
                    resultData[j][i] = noData;
                else
                    resultData[j][i] = grid.getElement(i, j) * value;
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute(MMAGrid grid1, MMAGrid grid2)  throws MMAFunctionInitException
    {
        if (( grid1 == null ) || ( grid2 == null ))
            throw new MMAFunctionInitException(this.getClass().toString() + " Grids are not initialized.");

        // Initialze the dimensions of grid 1 and the noData value
        int x = grid1.getX();
        int y = grid1.getY();
        float noData = grid1.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        float temp;
        
        // Ensure the grids have the identical dimensions
        if ((x != grid2.getX()) || (y != grid2.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
            {
                if ((grid1.getElement(i, j) == noData) || (grid2.getElement(i, j) == noData))
                    resultData[j][i] = noData;
                else
                    resultData[j][i] = grid1.getElement(i, j) * grid2.getElement(i, j);
            }
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute( MMAGridList gridList ) throws MMAFunctionInitException
    {
        MMAGrid baseGrid;
        MMAGrid tempGrid;

        if (gridList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is not initialized.");

        // Make sure the list is not empty
        if (gridList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is empty.");
        
        // Return the fist element if there is only one grid in the list
        if (gridList.size() == 1)
            return (MMAGrid)gridList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseGrid = (MMAGrid)gridList.get(0);
        
        // Initialze the dimensions of grid 1 and the noData value
        int x = baseGrid.getX();
        int y = baseGrid.getY();
        float noData = baseGrid.getNoData();
        int index;

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        float temp;
        
        // Ensure the grids have the identical dimensions
        for ( index=1; index < gridList.size(); index++)
        {
            tempGrid = (MMAGrid)gridList.get(index);
            if ((x != tempGrid.getX()) || (y != tempGrid.getY()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
            {
                // Check for noData value
                if (baseGrid.getElement(i,j) == noData)
                    resultData[j][i] = noData;
                else
                {
                    // Initialize temp with an element of base grid
                    temp = baseGrid.getElement(i,j);

                    for ( index=1; index < gridList.size(); index++)
                    {
                        tempGrid = (MMAGrid)gridList.get(index);
                        
                        // Check for noData value for each grid element
                        if (tempGrid.getElement(i, j) == noData)
                        {
                            resultData[j][i] = noData;
                            break;
                        }
                        else
                            // Multiply value of an element of the current grid with temp
                            temp *= tempGrid.getElement(i,j);
                    }
    
                    if (resultData[j][i] != noData)
                        resultData[j][i] = temp;
                }
            }
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
 
    public MMAGrid execute( MMAGrid grid1, MMAGrid grid2, XYLag lag) throws MMAFunctionInitException
    {
        if (( grid1 == null ) || ( grid2 == null ))
            throw new MMAFunctionInitException(this.getClass().toString() + " Grids are not initialized.");

        if ( lag == null )
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Initialze the dimensions of grid 1 and the noData value
        int x = grid1.getX();
        int y = grid1.getY();
        int xOffset, yOffset;
        float noData = grid1.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[grid1.getY()][grid1.getX()];
        float temp;
        
        // Ensure the grids have the identical dimensions
        if ((x != grid2.getX()) || (y != grid2.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
            {
                // Check if the indexes are within the boundaries
                if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                    resultData[j][i] = noData;
                else
                    if ((grid1.getElement(i,j) == noData) || (grid2.getElement(xOffset+i, yOffset+j) == noData))
                        resultData[j][i] = noData;
                    else
                        resultData[j][i] = grid1.getElement(i,j) * grid2.getElement(xOffset+i, yOffset+j);
            }
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute( MMAGridList gridList, XYLag lag) throws MMAFunctionInitException
    {
        MMAGrid baseGrid;
        MMAGrid tempGrid;

        if (gridList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is not initialized.");

        // Make sure the list is not empty
        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Make sure the list is not empty
        if (gridList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is empty");
        
        // Return the fist element if there is only one grid in the list
        if (gridList.size() == 1)
            return (MMAGrid)gridList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseGrid = (MMAGrid)gridList.get(0);
        
        // Initialze the dimensions of grid 1 and the noData value
        int x = baseGrid.getX();
        int y = baseGrid.getY();
        int xOffset, yOffset;
        float noData = baseGrid.getNoData();
        int index;

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        float temp;
        
        // Ensure the grids have the identical dimensions
        for ( index=1; index < gridList.size(); index++)
        {
            tempGrid = (MMAGrid)gridList.get(index);
            if ((x != tempGrid.getX()) || (y != tempGrid.getY()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
            {
                // Check if the indexes are within the boundaries
                if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                    resultData[j][i] = noData;
                else
                {
                    // Check for noData value
                    if (baseGrid.getElement(i,j) == noData)
                        resultData[j][i] = noData;
                    else
                    {
                        // Initialize temp with an element of base grid
                        temp= baseGrid.getElement(i,j);

                        for ( index=1; index < gridList.size(); index++)
                        {
                            tempGrid = (MMAGrid)gridList.get(index);

                            // Check for noData value for each grid element
                            if (tempGrid.getElement(xOffset+i,yOffset+j) == noData)
                            {
                                resultData[j][i] = noData;
                                break;
                            }
                            else
                                // Multiply value of an element of the current grid with temp
                                temp *= tempGrid.getElement(xOffset+i,yOffset+j);
                        }

                        if (resultData[j][i] != noData)
                            // Assign final value
                            resultData[j][i] = temp;
                    }
                }
            }
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute( MMAGridList gridList, XYLagList lagList) throws MMAFunctionInitException
    {
        MMAGrid baseGrid;
        MMAGrid tempGrid;
        XYLag tempLag;

        // Make sure the lists are initialized
        if (gridList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is not initialized.");

        if (lagList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is not initialized.");

        // Make sure the list are not empty
        if (gridList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list is empty.");
        
        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is empty.");

        // Make sure the lag list has the same number of element as the grid list
        if (gridList.size() != (lagList.size()))
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid list and lag list don't have the same number of elemtents");

        // Return the fist element if there is only one grid in the list
        if (gridList.size() == 1)
            return (MMAGrid)gridList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseGrid = (MMAGrid)gridList.get(0);
        
        // Initialze the dimensions of grid 1 and the noData value
        int x = baseGrid.getX();
        int y = baseGrid.getY();
        int xOffset, yOffset;
        int baseXOffset, baseYOffset;
        float noData = baseGrid.getNoData();
        int index;

        // initialize the baseXOffset and baseYOffset
        tempLag = (XYLag)lagList.get(0);
        baseXOffset = tempLag.getX();
        baseYOffset = tempLag.getY();
        
        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        float temp;
        
        // Ensure the grids have the identical dimensions
        for ( index=1; index < gridList.size(); index++)
        {
            tempGrid = (MMAGrid)gridList.get(index);
            if ((x != tempGrid.getX()) || (y != tempGrid.getY()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
            {
                // Check if the indexes for the first grid are within the boundaries
                if (((baseXOffset + i) < 0 ) || ((baseYOffset + j) < 0 ) || ((baseXOffset + i) >= x ) || ((baseYOffset + j) >= y ))
                    resultData[j][i] = noData;
                else
                {
                    // Check for noData value of the base grid
                    if (baseGrid.getElement(i+baseXOffset,j+baseYOffset) == noData)
                        resultData[j][i] = noData;
                    else
                    {
                        // Initialize temp with an element of base grid
                        temp = baseGrid.getElement(i+baseXOffset,j+baseYOffset);

                        for ( index=1; index < gridList.size(); index++)
                        {
                            tempGrid = (MMAGrid)gridList.get(index);
                            tempLag = (XYLag)lagList.get(index - 1);
                            xOffset = tempLag.getX();
                            yOffset = tempLag.getY();

                            // Check if the indexes are within the boundaries
                            if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                                resultData[j][i] = noData;
                            else
                                // Check for noData value for each grid element
                                if (tempGrid.getElement(xOffset+i,yOffset+j) == noData)
                                {
                                    resultData[j][i] = noData;
                                    break;
                                }
                                else
                                    // Multiply value of an element of the current grid with temp
                                    temp *= tempGrid.getElement(xOffset+i,yOffset+j);
                        }

                        if (resultData[j][i] != noData)
                            // Assign final value
                            resultData[j][i] = temp;
                    }
                }
            }
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    /* MMA Time Cube Functions */
    public MMATimeCube execute( MMATimeCube tCube, float value) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    if (tCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = tCube.getElement(i,j,k) * value;
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMATimeSeries timeSeries) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the t dimension match with those of the time cube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    if ((tCube.getElement(i,j,k) == noData) || (timeSeries.getElement(k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = tCube.getElement(i,j,k) * timeSeries.getElement(k);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the time cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    if ((tCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = tCube.getElement(i,j,k) * grid.getElement(i,j);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMATimeCube execute( MMATimeCube tCube1, MMATimeCube tCube2) throws MMAFunctionInitException
    {
        if (( tCube1 == null) || ( tCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cubes are not initialized.");    

        // Initialze the dimensions of timecube 1 and the noData value
        int x = tCube1.getX();
        int y = tCube1.getY();
        int t = tCube1.getT();
        float noData = tCube1.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the time cubes have the identical dimensions
        if ((x != tCube2.getX()) || (y != tCube2.getY()) || (t != tCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check for noData value
                    if ((tCube1.getElement(i,j,k) == noData) || (tCube2.getElement(i,j,k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = tCube1.getElement(i,j,k) * tCube2.getElement(i,j,k);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMATimeCube execute( MMATimeCubeList cubeList ) throws MMAFunctionInitException
    {
        MMATimeCube baseCube;
        MMATimeCube tempCube;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is not initialized.");    
        
        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is empty");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMATimeCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMATimeCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int t = baseCube.getT();
        float noData = baseCube.getNoData();       

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the time cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMATimeCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<t; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check for noData value
                    if (baseCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                    {
                        // Initialize temp with an element of base cube
                        temp = baseCube.getElement(i,j,k);

                        for ( int index=1; index < cubeList.size(); index++)
                        {
                            tempCube = (MMATimeCube)cubeList.get(index);
                            // Check for noData value for each cube element
                            if (tempCube.getElement(i,j,k) == noData)
                            {
                                resultData[k][j][i] = noData;
                                break;
                            }
                            else
                                temp *= tempCube.getElement(i,j,k);
                        }
    
                        if (resultData[k][j][i] != noData)
                            resultData[k][j][i] = temp;
                    }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMATimeSeries timeSeries, TLag lag) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        int tOffset;
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the t dimension match with those of the time cube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        tOffset = lag.getT();

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((tOffset + k) < 0 ) || ((tOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                        if ((tCube.getElement(i,j,k) == noData) || (timeSeries.getElement(tOffset + k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = tCube.getElement(i,j,k) * timeSeries.getElement(tOffset + k);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    public MMATimeCube execute( MMATimeCube tCube, MMAGrid grid, XYLag lag) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        int xOffset, yOffset;
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        // Ensure the x and y dimensions match with those of the time cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                        resultData[k][j][i] = noData;
                    else
                        if ((tCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = tCube.getElement(i,j,k) * grid.getElement(xOffset + i, yOffset + j);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    public MMATimeCube execute( MMATimeCube tCube1, MMATimeCube tCube2, XYTLag lag) throws MMAFunctionInitException
    {
        if (( tCube1 == null) || ( tCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cubes are not initialized.");    
        
        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube 1 and the noData value
        int x = tCube1.getX();
        int y = tCube1.getY();
        int t = tCube1.getT();
        int xOffset, yOffset, tOffset;
        float noData = tCube1.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        tOffset = lag.getT();

        // Ensure the time cubes have the identical dimensions
        if ((x != tCube2.getX()) || (y != tCube2.getY()) || (t != tCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if ((tCube1.getElement(i,j,k) == noData) || (tCube2.getElement(i,j,k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = tCube1.getElement(i,j,k) * tCube2.getElement(xOffset + i, yOffset + j, tOffset + k);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    public MMATimeCube execute( MMATimeCubeList cubeList, XYTLag lag ) throws MMAFunctionInitException
    {
        MMATimeCube baseCube;
        MMATimeCube tempCube;

        // Make sure the list is initialized
        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is empty.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMATimeCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMATimeCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int t = baseCube.getT();
        int xOffset, yOffset, tOffset;
        float noData = baseCube.getNoData();       

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        tOffset = lag.getT();

        // Ensure the time cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMATimeCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<t; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if (baseCube.getElement(i,j,k) == noData)
                            resultData[k][j][i] = noData;
                        else
                        {
                            // Initialize temp with an element of base cube
                            temp = baseCube.getElement(i,j,k);

                            for ( int index=1; index < cubeList.size(); index++)
                            {
                                tempCube = (MMATimeCube)cubeList.get(index);
                                // Check for noData value for each cube element
                                if (tempCube.getElement(xOffset + i, yOffset + j, tOffset + k) == noData)
                                {
                                    resultData[k][j][i] = noData;
                                    break;
                                }
                                else
                                    temp *= tempCube.getElement(xOffset + i, yOffset + j, tOffset + k);
                            }

                            if (resultData[k][j][i] != noData)
                                resultData[k][j][i] = temp;
                        }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMATimeCube execute( MMATimeCubeList cubeList, XYTLagList lagList) throws MMAFunctionInitException
    {
        MMATimeCube baseCube;
        MMATimeCube tempCube;
        XYTLag tempLag;

        // Make sure the list is initialized
        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is not initialized.");    

        if (lagList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is empty.");    
        
        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list is empty.");    

        // Make sure the lag list has the same number of element as the cube list
        if (cubeList.size() != (lagList.size()))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube list and lag list don't have the same number of element.");    

        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMATimeCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMATimeCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int t = baseCube.getT();
        int xOffset, yOffset, tOffset;
        int baseXOffset, baseYOffset, baseTOffset;
        float noData = baseCube.getNoData();       

        // initialize the baseXOffset, baseYOffset and baseTOffset
        tempLag = (XYTLag)lagList.get(0);
        baseXOffset = tempLag.getX();
        baseYOffset = tempLag.getY();
        baseTOffset = tempLag.getT();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the time cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMATimeCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<t; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check if the indexes for the first cube are within the boundaries
                    if (((baseXOffset + i) < 0 ) || ((baseYOffset + j) < 0 ) || ((baseTOffset + k) < 0 ) || ((baseXOffset + i) >= x ) || ((baseYOffset + j) >= y ) || ((baseTOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                    {
                        // Check for noData value
                        if (baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseTOffset) == noData)
                            resultData[k][j][i] = noData;
                        else
                        {
                            // Initialize temp with an element of base cube
                            temp = baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseTOffset);

                            for ( int index=1; index < cubeList.size(); index++)
                            {
                                tempCube = (MMATimeCube)cubeList.get(index);
                                tempLag = (XYTLag)lagList.get(index-1);

                                xOffset = tempLag.getX();
                                yOffset = tempLag.getY();
                                tOffset = tempLag.getT();

                                // Check if the indexes are within the boundaries
                                if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + k) >= t ))
                                    resultData[k][j][i] = noData;
                                else
                                    // Check for noData value for each cube element
                                    if (tempCube.getElement(xOffset + i, yOffset + j, tOffset + k) == noData)
                                    {
                                        resultData[k][j][i] = noData;
                                        break;
                                    }
                                    else
                                        temp *= tempCube.getElement(xOffset + i, yOffset + j, tOffset + k);
                            }

                            if (resultData[k][j][i] != noData)
                                resultData[k][j][i] = temp;
                        }
                    }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    // Spacecube functions

    public MMASpaceCube execute( MMASpaceCube sCube, float value) throws MMAFunctionInitException
    {
        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        float noData = sCube.getNoData();   
         
        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    if (sCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = sCube.getElement(i,j,k) * value;
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if ( sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        float noData = sCube.getNoData();   
         
        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;

        // Ensure the x and y dimensions match with those of the space cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    if ((sCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = sCube.getElement(i,j,k) * grid.getElement(i,j);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube1, MMASpaceCube sCube2) throws MMAFunctionInitException
    {
        if ((sCube1 == null) || (sCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cubes are not initialized.");    
        
        // Initialze the dimensions of the spacecube1 and the noData value
        int x = sCube1.getX();
        int y = sCube1.getY();
        int z = sCube1.getZ();
        float noData = sCube1.getNoData();   

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        // Ensure the space cubes have the identical dimensions
        if ((x != sCube2.getX()) || (y != sCube2.getY()) || (z != sCube2.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check for noData value
                    if ((sCube1.getElement(i,j,k) == noData) || (sCube2.getElement(i,j,k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = sCube1.getElement(i,j,k) * sCube2.getElement(i,j,k);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCubeList cubeList ) throws MMAFunctionInitException
    {
        MMASpaceCube baseCube;
        MMASpaceCube tempCube;

        // Make sure the list is not empty
        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is empty.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMASpaceCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMASpaceCube)cubeList.get(0);
        
        // Initialze the dimensions of the spacecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        float noData = baseCube.getNoData();       

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMASpaceCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<z; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check for noData value
                    if (baseCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                    {
                        // Initialize temp with an element of base cube
                        temp = baseCube.getElement(i,j,k);

                        for ( int index=1; index < cubeList.size(); index++)
                        {
                            tempCube = (MMASpaceCube)cubeList.get(index);
                            // Check for noData value for each cube element
                            if (tempCube.getElement(i,j,k) == noData)
                            {
                                resultData[k][j][i] = noData;
                                break;
                            }
                            else
                                temp *= tempCube.getElement(i,j,k);
                        }
    
                        if (resultData[k][j][i] != noData)
                            resultData[k][j][i] = temp;
                    }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }

    public MMASpaceCube execute( MMASpaceCube sCube, MMAGrid grid, XYLag lag)  throws MMAFunctionInitException
    {
        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        int xOffset, yOffset;
        float noData = sCube.getNoData();

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        // Ensure the x and y dimensions match with those of the space cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                        resultData[k][j][i] = noData;
                    else
                        if ((sCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = sCube.getElement(i,j,k) * grid.getElement(xOffset + i, yOffset + j);
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube1, MMASpaceCube sCube2, XYZLag lag) throws MMAFunctionInitException
    {
        if ((sCube1 == null) || (sCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cubes are not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube1.getX();
        int y = sCube1.getY();
        int z = sCube1.getZ();
        int xOffset, yOffset, zOffset;
        float noData = sCube1.getNoData();   

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
 
        // Ensure the space cubes have the identical dimensions
        if ((x != sCube2.getX()) || (y != sCube2.getY()) || (z != sCube2.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        
        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check if the indexes are within the boundaries
                     if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if ((sCube1.getElement(i,j,k) == noData) || (sCube2.getElement(i,j,k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = sCube1.getElement(i,j,k) * sCube2.getElement(i,j,k);
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCubeList cubeList, XYZLag lag )  throws MMAFunctionInitException
    {
        MMASpaceCube baseCube;
        MMASpaceCube tempCube;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is empty.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMASpaceCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMASpaceCube)cubeList.get(0);
        
        // Initialze the dimensions of the spacecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        int xOffset, yOffset, zOffset;
        float noData = baseCube.getNoData();       

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();

        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMASpaceCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<z; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k) >= z ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if (baseCube.getElement(i,j,k) == noData)
                            resultData[k][j][i] = noData;
                        else
                        {
                            // Initialize temp with an element of base cube
                            temp = baseCube.getElement(i,j,k);

                            for ( int index=1; index < cubeList.size(); index++)
                            {
                                tempCube = (MMASpaceCube)cubeList.get(index);
                                // Check for noData value for each cube element
                                if (tempCube.getElement(xOffset + i, yOffset + j, zOffset + k) == noData)
                                {
                                    resultData[k][j][i] = noData;
                                    break;
                                }
                                else
                                    temp *= tempCube.getElement(xOffset + i, yOffset + j, zOffset + k);
                            }

                            if (resultData[k][j][i] != noData)
                                // Assign final value
                                resultData[k][j][i] = temp;
                        }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
 
    public MMASpaceCube execute( MMASpaceCubeList cubeList, XYZLagList lagList) throws MMAFunctionInitException
    {
        MMASpaceCube baseCube;
        MMASpaceCube tempCube;
        XYZLag tempLag;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is not initialized.");    

        if (lagList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is empty.");    
        
        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is empty.");    
        
        // Make sure the lag list has the same number of element as the cube list
        if (cubeList.size() != (lagList.size()))
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list and lag list don't have the same number of element.");    

        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMASpaceCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMASpaceCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        int xOffset, yOffset, zOffset;
        int baseXOffset, baseYOffset, baseZOffset;
        float noData = baseCube.getNoData();       

        // initialize the baseXOffset, baseYOffset and baseTOffset
        tempLag = (XYZLag)lagList.get(0);
        baseXOffset = tempLag.getX();
        baseYOffset = tempLag.getY();
        baseZOffset = tempLag.getZ();

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMASpaceCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int k=0; k<z; k++)
            for ( int j=0; j <y; j++)
                for ( int i=0; i <x; i++)
                {
                    // Check if the indexes for the first cube are within the boundaries
                    if (((baseXOffset + i) < 0 ) || ((baseYOffset + j) < 0 ) || ((baseZOffset + k) < 0 ) || ((baseXOffset + i) >= x ) || ((baseYOffset + j) >= y ) || ((baseZOffset + k) >= z ))
                        resultData[k][j][i] = noData;
                    else
                    {
                        // Check for noData value
                        if (baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseZOffset) == noData)
                            resultData[k][j][i] = noData;
                        else
                        {
                            // Initialize temp with an element of  base cube 
                            temp = baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseZOffset);

                            for ( int index=1; index < cubeList.size(); index++)
                            {
                                tempCube = (MMASpaceCube)cubeList.get(index);
                                tempLag = (XYZLag)lagList.get(index-1);

                                xOffset = tempLag.getX();
                                yOffset = tempLag.getY();
                                zOffset = tempLag.getZ();

                                // Check if the indexes are within the boundaries
                                if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k) >= z ))
                                    resultData[k][j][i] = noData;
                                else
                                    // Check for noData value for each cube element
                                    if (tempCube.getElement(xOffset + i, yOffset + j, zOffset + k) == noData)
                                    {
                                        resultData[k][j][i] = noData;
                                        break;
                                    }
                                    else
                                        temp *= tempCube.getElement(xOffset + i, yOffset + j, zOffset + k);
                            }

                            if (resultData[k][j][i] != noData)
                                // Assign final value
                                resultData[k][j][i] = temp;
                        }
                    }
                }
                    
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    // Hypercube functions
    public MMAHyperCube execute( MMAHyperCube hCube, float value) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        if (hCube.getElement(i,j,k,l) == noData)
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * value;
                    }

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
        
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeSeries timeSeries) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        if ((hCube.getElement(i,j,k,l) == noData) || (timeSeries.getElement(l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * timeSeries.getElement(l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        if ((hCube.getElement(i,j,k,l) == noData) || (grid.getElement(i,j) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * grid.getElement(i,j);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeCube tCube) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input time cube match that of the hypercube
        if ((x != tCube.getX()) || (y != tCube.getY()) || (t != tCube.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int l=0; l<t; l++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check for noData value
                        if ((hCube.getElement(i,j,k,l) == noData) || (tCube.getElement(i,j,l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * tCube.getElement(i,j,l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMASpaceCube sCube) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input space cube match that of the hypercube
        if ((x != sCube.getX()) || (y != sCube.getY()) || (z != sCube.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check for noData value
                        if ((hCube.getElement(i,j,k,l) == noData) || (sCube.getElement(i,j,k) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * sCube.getElement(i,j,k);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube1, MMAHyperCube hCube2) throws MMAFunctionInitException
    {
        if ((hCube1 == null) || (hCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cubes are not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube1.getX();
        int y = hCube1.getY();
        int z = hCube1.getZ();
        int t = hCube1.getZ();
        float noData = hCube1.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;

        // Ensure the hyper cubes have the identical dimensions
        if ((x != hCube2.getX()) || (y != hCube2.getY()) || (z != hCube2.getZ()) || (t != hCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check for noData value
                        if ((hCube1.getElement(i,j,k,l) == noData) || (hCube2.getElement(i,j,k,l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = hCube1.getElement(i,j,k,l) * hCube2.getElement(i,j,k,l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCubeList cubeList ) throws MMAFunctionInitException
    {
        MMAHyperCube baseCube;
        MMAHyperCube tempCube;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is empty.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMAHyperCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMAHyperCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        int t = baseCube.getT();
        float noData = baseCube.getNoData();       

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMAHyperCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int l=0; l<t; l++)
	    for ( int k=0; k<z; k++)
                for ( int j=0; j <y; j++)
                    for ( int i=0; i <x; i++)
                    {
                        // Check for noData value
                        if (baseCube.getElement(i,j,k,l) == noData)
                            resultData[l][k][j][i] = noData;
                        else
                        {
                            // Initialize temp with value of an element of base cube
                            temp = baseCube.getElement(i,j,k,l);

                            for ( int index=1; index < cubeList.size(); index++)
                            {
                                tempCube = (MMAHyperCube)cubeList.get(index);
                                // Check for noData value for each cube element
                                if (tempCube.getElement(i,j,k,l) == noData)
                                {
                                    resultData[l][k][j][i] = noData;
                                    break;
                                }
                                else
                                    temp *= tempCube.getElement(i,j,k,l);
                            }
    
                            if (resultData[l][k][j][i] != noData)
                                // Assign final value
                                resultData[l][k][j][i] = temp;
                        }
                    }

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeSeries timeSeries, TLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int tOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        tOffset = lag.getT();

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check if the indexes are within the boundaries
                        if (((tOffset + l) < 0 ) || ((tOffset + l) >= t ))
                            resultData[l][k][j][i] = noData;
                        else
                            if ((hCube.getElement(i,j,k,l) == noData) || (timeSeries.getElement(tOffset + l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * timeSeries.getElement(tOffset + l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMAGrid grid, XYLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check if the indexes are within the boundaries
                        if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                            resultData[l][k][j][i] = noData;
                        else
                            if ((hCube.getElement(i,j,k,l) == noData) || (grid.getElement(i,j) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * grid.getElement(xOffset + i,yOffset + j);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeCube tCube, XYTLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset, tOffset;
        float noData = hCube.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input time cube match that of the hypercube
        if ((x != tCube.getX()) || (y != tCube.getY()) || (t != tCube.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        tOffset = lag.getT();

        for ( int k=0; k<z; k++)
            for ( int l=0; l<t; l++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check if the indexes are within the boundaries
                        if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + l) >= t ))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube.getElement(i,j,k,l) == noData) || (tCube.getElement(i,j,l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * tCube.getElement(xOffset + i,yOffset + j,tOffset + l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMASpaceCube sCube, XYZLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset, zOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input space cube match that of the hypercube
        if ((x != sCube.getX()) || (y != sCube.getY()) || (z != sCube.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check if the indexes are within the boundaries
                         if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z ))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube.getElement(i,j,k,l) == noData) || (sCube.getElement(i,j,k) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = hCube.getElement(i,j,k,l) * sCube.getElement(xOffset + i, yOffset + j, zOffset + k);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube1, MMAHyperCube hCube2, XYZTLag lag) throws MMAFunctionInitException
    {
        if ((hCube1 == null) || (hCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cubes are not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube1.getX();
        int y = hCube1.getY();
        int z = hCube1.getZ();
        int t = hCube1.getZ();
        int xOffset, yOffset, zOffset, tOffset;
        float noData = hCube1.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;

        // Ensure the hyper cubes have the identical dimensions
        if ((x != hCube2.getX()) || (y != hCube2.getY()) || (z != hCube2.getZ()) || (t != hCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        tOffset = lag.getT();
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                    {
                        // Check if the indexes are within the boundaries
                         if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z )|| ((tOffset + l)>=t))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube1.getElement(i,j,k,l) == noData) || (hCube2.getElement(i,j,k,l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] =  hCube1.getElement(i,j,k,l) * hCube2.getElement(xOffset + i,yOffset + j,zOffset + k,tOffset + l);
                    }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCubeList cubeList, XYZTLag lag) throws MMAFunctionInitException
    {
        MMAHyperCube baseCube;
        MMAHyperCube tempCube;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is empty.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMAHyperCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMAHyperCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        int t = baseCube.getT();
        int xOffset, yOffset, zOffset, tOffset;
        float noData = baseCube.getNoData();       

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        tOffset = lag.getT();

        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMAHyperCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int l=0; l<t; l++)
	    for ( int k=0; k<z; k++)
                for ( int j=0; j <y; j++)
                    for ( int i=0; i <x; i++)
                    {
                        // Check if the indexes are within the boundaries
                         if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z )|| ((tOffset + l)>=t))
                            resultData[l][k][j][i] = noData;
                         else
                            // Check for noData value
                            if (baseCube.getElement(i,j,k,l) == noData)
                                resultData[l][k][j][i] = noData;
                            else
                            {
                                // Initialize temp with value of an element of base cube
                                temp = baseCube.getElement(i,j,k,l);

                                for ( int index=1; index < cubeList.size(); index++)
                                {
                                    tempCube = (MMAHyperCube)cubeList.get(index);
                                    // Check for noData value for each cube element
                                    if (tempCube.getElement(xOffset+i, yOffset+j, zOffset+k, tOffset+l) == noData)
                                    {
                                        resultData[l][k][j][i] = noData;
                                        break;
                                    }
                                    else
                                        temp *= tempCube.getElement(xOffset+i, yOffset+j, zOffset+k, tOffset+l);
                                }

                                if (resultData[l][k][j][i] != noData)
                                    // Assign final value
                                    resultData[l][k][j][i] = temp;
                            }
                    }

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
       
    }
    
    public MMAHyperCube execute( MMAHyperCubeList cubeList, XYZTLagList lagList) throws MMAFunctionInitException
    {
        MMAHyperCube baseCube;
        MMAHyperCube tempCube;
        XYZTLag tempLag;

        if (cubeList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is not initialized.");    

        if (lagList == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list is not initialized.");    

        // Make sure the list is not empty
        if (cubeList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list is empty.");    
        
        if (lagList.size() == 0)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag list list is empty.");    

        // Make sure the lag list has the same number of element as the cube list
        if (cubeList.size() != (lagList.size()))
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube list and lag list don't have the same number of element.");    
        
        // Return the fist element if there is only one cube in the list
        if (cubeList.size() == 1)
            return (MMAHyperCube)cubeList.get(0);

        // Retrieve the first element from the list to be the base grid
        baseCube = (MMAHyperCube)cubeList.get(0);
        
        // Initialze the dimensions of the timecube and the noData value
        int x = baseCube.getX();
        int y = baseCube.getY();
        int z = baseCube.getZ();
        int t = baseCube.getT();
        int xOffset, yOffset, zOffset, tOffset;
        int baseXOffset, baseYOffset, baseZOffset, baseTOffset;
        float noData = baseCube.getNoData();       

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // initialize the baseXOffset, baseYOffset, baseZOffset and baseTOffset
        tempLag = (XYZTLag)lagList.get(0);
        baseXOffset = tempLag.getX();
        baseYOffset = tempLag.getY();
        baseZOffset = tempLag.getZ();
        baseTOffset = tempLag.getT();

        // Ensure the space cubes have the identical dimensions
        for ( int index=1; index < cubeList.size(); index++)
        {
            tempCube = (MMAHyperCube)cubeList.get(index);
            if ((x != tempCube.getX()) || (y != tempCube.getY()) || (z != tempCube.getZ()) || (t != tempCube.getT()))
                throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
        }    
        
        for ( int l=0; l<t; l++)
	    for ( int k=0; k<z; k++)
                for ( int j=0; j <y; j++)
                    for ( int i=0; i <x; i++)
                    {
                        // Check if the indexes for the first cube are within the boundaries
                        if (((baseXOffset + i) < 0 ) || ((baseYOffset + j) < 0 ) || ((baseZOffset + k) < 0 ) || ((baseTOffset + l) < 0 ) || ((baseXOffset + i) >= x ) || ((baseYOffset + j) >= y ) || ((baseZOffset + k) >= z ) || ((baseTOffset + l) >= z ))
                            resultData[l][k][j][i] = noData;
                        else
                        {
                            // Check for noData value
                            if (baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseZOffset,l+baseTOffset) == noData)
                                resultData[l][k][j][i] = noData;
                            else
                            {
                                // Initialize temp with value of an element of the base cube
                                temp = baseCube.getElement(i+baseXOffset,j+baseYOffset,k+baseZOffset,l+baseTOffset);

                                for ( int index=1; index < cubeList.size(); index++)
                                {
                                    tempCube = (MMAHyperCube)cubeList.get(index);
                                    tempLag = (XYZTLag)lagList.get(index-1);

                                    xOffset = tempLag.getX();
                                    yOffset = tempLag.getY();
                                    zOffset = tempLag.getZ();
                                    tOffset = tempLag.getT();

                                    // Check if the indexes are within the boundaries
                                     if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z )|| ((tOffset + l)>=t))
                                        resultData[l][k][j][i] = noData;
                                     else
                                        // Check for noData value for each cube element
                                        if (tempCube.getElement(xOffset+i, yOffset+j, zOffset+k, tOffset+l) == noData)
                                        {
                                            resultData[l][k][j][i] = noData;
                                            break;
                                        }
                                        else
                                            temp *= tempCube.getElement(xOffset+i, yOffset+j, zOffset+k, tOffset+l);
                                }

                                if (resultData[l][k][j][i] != noData)
                                    // Assign final value
                                    resultData[l][k][j][i] = temp;
                            }
                        }
                    }

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        return outputCube;
       
    }    
}
